package com.woniu.service;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.RemoveObjectsArgs;
import io.minio.Result;
import io.minio.SetBucketPolicyArgs;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

@Slf4j
@Service
public class MinioService {
    @Autowired
    private MinioClient minioClient;

    @Value("${minio.bucketName}")
    private String bucketName;

    @Value("${minio.endpoint}")
    private String endpoint;

    /**
     * 服务启动时执行，设置MinIO存储桶的公共访问策略
     */
    @PostConstruct
    public void init() {
        try {
            // 设置允许公共访问的存储桶策略
            // 这将使得通过直接URL可以访问文件，无需签名
            String policy = String.format(
                    "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::%s/*\"]}]}",
                    bucketName
            );
            minioClient.setBucketPolicy(
                    SetBucketPolicyArgs.builder()
                            .bucket(bucketName)
                            .config(policy)
                            .build()
            );
            log.info("MinIO bucket policy set successfully for bucket: {}", bucketName);
        } catch (Exception e) {
            log.error("Error setting MinIO bucket policy for bucket: {}", bucketName, e);
            // 这里可以根据实际情况选择抛出异常或进行其他处理
        }
    }

    /**
     * 上传单个视频文件，支持四级课程层级结构
     * @param file FilePart对象，包含上传的文件
     * @param mainCategory 课程大类
     * @param subCategory 课程小类
     * @param chapter 章节
     * @param course 具体课程
     * @return Mono<String> 包含MinIO中视频的完整objectName
     */
    public Mono<String> uploadVideo(FilePart file, String mainCategory, String subCategory, String chapter, String course) {
        return Mono.fromCallable(() -> {
            // 生成唯一文件名，保留原始文件扩展名
            String fileExtension = getFileExtension(file.filename());
            String uniqueFileName = UUID.randomUUID().toString() + fileExtension;

            // 拼接成 MinIO 的 objectName (四级目录结构)
            String objectName = String.format("%s/%s/%s/%s/%s", mainCategory, subCategory, chapter, course, uniqueFileName);

            // 将 FilePart 转换为 InputStream
            return DataBufferUtils.join(file.content())
                .map(dataBuffer -> {
                    try {
                        byte[] bytes = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(bytes);
                        ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
                        
                        // 执行文件上传
                        minioClient.putObject(PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(inputStream, bytes.length, -1)
                            .contentType(file.headers().getContentType().toString())
                            .build());
                        
                        DataBufferUtils.release(dataBuffer);
                        return objectName;
                    } catch (Exception e) {
                        throw new RuntimeException("Failed to upload file to MinIO", e);
                    }
                })
                .block(); // 等待所有数据块处理完成
        }).subscribeOn(Schedulers.boundedElastic());
    }

    /**
     * 上传单个视频文件，支持四级课程层级结构（向后兼容）
     * @param file MultipartFile对象，包含上传的文件
     * @param mainCategory 课程大类
     * @param subCategory 课程小类
     * @param chapter 章节
     * @param course 具体课程
     * @return MinIO中视频的完整objectName
     * @throws Exception 上传过程中可能发生的异常
     */
    public String uploadVideo(MultipartFile file, String mainCategory, String subCategory, String chapter, String course) throws Exception {
        // 生成唯一文件名，保留原始文件扩展名
        String fileExtension = getFileExtension(file.getOriginalFilename());
        String uniqueFileName = UUID.randomUUID().toString() + fileExtension;

        // 拼接成 MinIO 的 objectName (四级目录结构)
        String objectName = String.format("%s/%s/%s/%s/%s", mainCategory, subCategory, chapter, course, uniqueFileName);

        InputStream inputStream = file.getInputStream();

        // 执行文件上传
        minioClient.putObject(PutObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .stream(inputStream, file.getSize(), -1)
                .contentType(file.getContentType())
                .build());

        log.info("文件 {} 成功上传到 MinIO，objectName: {}", file.getOriginalFilename(), objectName);
        return objectName;
    }

    /**
     * 上传单个视频文件，支持 FilePart（保持向后兼容）
     * @param file FilePart对象，包含上传的文件
     * @param category 课程大类
     * @param course 课程
     * @param chapter 章节
     * @return Mono<String> 包含MinIO中视频的完整objectName
     */
    public Mono<String> uploadVideo(FilePart file, String category, String course, String chapter) {
        return uploadVideo(file, category, course, chapter, "default");
    }

    /**
     * 上传单个视频文件，支持 MultipartFile（保持向后兼容）
     * @param file MultipartFile对象，包含上传的文件
     * @param category 课程大类
     * @param course 课程
     * @param chapter 章节
     * @return MinIO中视频的完整objectName
     * @throws Exception 上传过程中可能发生的异常
     */
    public String uploadVideo(MultipartFile file, String category, String course, String chapter) throws Exception {
        return uploadVideo(file, category, course, chapter, "default");
    }

    /**
     * 批量上传视频文件，并根据分类信息生成多级目录
     * @param files MultipartFile列表
     * @param category 课程大类
     * @param course 课程
     * @param chapter 章节
     * @return 包含每个上传视频文件名和URL的列表
     * @throws Exception 批量上传过程中可能发生的异常
     */
    public List<Map<String, String>> uploadVideos(List<MultipartFile> files, String category, String course, String chapter) throws Exception {
        List<Map<String, String>> resultList = new ArrayList<>();
        for (MultipartFile file : files) {
            try {
                String objectName = uploadVideo(file, category, course, chapter); // 调用单个上传方法
                String url = getVideoUrl(objectName);
                Map<String, String> map = new HashMap<>();
                map.put("fileName", objectName);
                map.put("url", url);
                resultList.add(map);
            } catch (Exception e) {
                log.error("批量上传中单个文件 {} 失败", file.getOriginalFilename(), e);
                // 这里可以选择是继续上传其他文件，还是直接抛出异常中断
                // 为了健壮性，通常会记录错误并继续，最终返回成功的列表
            }
        }
        return resultList;
    }

    /**
     * 获取视频的直接访问URL
     * @param fileName 视频在MinIO中的完整objectName
     * @return 视频的公开访问URL
     */
    public String getVideoUrl(String fileName) {
        // MinIO的endpoint通常已经包含了协议和主机名，直接拼接即可
        return String.format("%s/%s/%s", endpoint, bucketName, fileName);
    }

    /**
     * 删除单个视频文件
     * @param fileName 要删除的视频在MinIO中的完整objectName
     * @throws Exception 删除过程中可能发生的异常
     */
    public void deleteVideo(String fileName) throws Exception {
        minioClient.removeObject(RemoveObjectArgs.builder()
                .bucket(bucketName)
                .object(fileName)
                .build());
        log.info("成功删除 MinIO 中的视频: {}", fileName);
    }

    /**
     * 批量删除视频文件
     * @param fileNames 要删除的视频objectName列表
     * @throws Exception 批量删除过程中可能发生的异常
     */
    public void deleteVideos(List<String> fileNames) throws Exception {
        // 将文件名列表转换为MinIO需要的DeleteObject列表
        List<DeleteObject> objectsToDelete = fileNames.stream()
                .map(DeleteObject::new)
                .collect(Collectors.toList());

        // 调用MinIO客户端的批量删除方法
        Iterable<Result<DeleteError>> results = minioClient.removeObjects(
                RemoveObjectsArgs.builder()
                        .bucket(bucketName)
                        .objects(objectsToDelete)
                        .build()
        );

        // 检查批量删除的结果，处理可能发生的错误
        for (Result<DeleteError> result : results) {
            DeleteError error = result.get();
            if (error != null) {
                log.error("批量删除时遇到错误，文件: {}，错误码: {}，错误信息: {}",
                        error.objectName(), error.code(), error.message());
                // 可以选择抛出异常，或收集所有错误信息返回
                throw new Exception("批量删除部分文件失败: " + error.objectName() + " - " + error.message());
            }
        }
        log.info("成功批量删除 MinIO 中的 {} 个视频", fileNames.size());
    }

    /**
     * 获取文件扩展名
     * @param fileName 文件名
     * @return 文件扩展名（包含点号）
     */
    private String getFileExtension(String fileName) {
        if (fileName == null || fileName.lastIndexOf(".") == -1) {
            return "";
        }
        return fileName.substring(fileName.lastIndexOf("."));
    }
}